using System;
using System.Collections.Generic;
using UnityEngine;
using MTBase;

public class MonoSingle<T> : MonoBehaviour where T : MonoBehaviour 
{
    public static T _ins;
    public static T Ins
    {
        get
        {
            if(_ins == null)
            {
                _ins = new GameObject().AddComponent<T>();
                DontDestroyOnLoad(_ins);
            }
            return _ins;
        }
    }

    public void Awake()
    {
        _ins = this as T;
    }

    public virtual void OnAwake()
    {
        
    }
}

public class MTTimer : MonoBehaviour
{
    
    
    private uint m_nNextTimerId = 1;
    private ulong m_unTick = 0;
    private ulong m_nFrameCount = 0;
    private KeyedPriorityQueue<uint, AbsTimerData, ulong> m_queue;
    private KeyedPriorityQueue<uint, AbsTimerData, ulong> m_FrameQueue;
    private Dictionary<uint, AbsTimerData> m_dict;

    /// <summary>
    /// 私有构造函数，封闭实例化。
    /// </summary>
    // private MTTimer()
    // {
    //     m_queue = new KeyedPriorityQueue<uint, AbsTimerData, ulong>();
    //     m_FrameQueue = new KeyedPriorityQueue<uint, AbsTimerData, ulong>();
    //     m_dict = new Dictionary<uint, AbsTimerData>();
    // }

    public void Awake()
    {
        m_queue = new KeyedPriorityQueue<uint, AbsTimerData, ulong>();
        m_FrameQueue = new KeyedPriorityQueue<uint, AbsTimerData, ulong>();
        m_dict = new Dictionary<uint, AbsTimerData>();
    }

    /// <summary>
    /// 添加单次定时对象
    /// </summary>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceTimer(uint start,  Action handler)
    {
        return AddTimer(m_queue, start, 0, handler, 0, false, m_unTick, TimerType.Time);

    }

    /// <summary>
    /// 添加单次定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceTimer<T>(uint start, Action<T> handler, T arg1)
    {
        return AddTimer(m_queue, start, 0, handler, 0, false,arg1, m_unTick, TimerType.Time);

    }

    /// <summary>
    /// 添加单次定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceTimer<T, U>(uint start, Action<T, U> handler, T arg1, U arg2, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, 0, handler, 0,false, arg1, arg2, m_unTick, TimerType.Time);
    }

    /// <summary>
    /// 添加单次定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <typeparam name="V">参数类型3</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="arg3">参数3</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceTimer<T, U, V>(uint start, Action<T, U, V> handler,  T arg1, U arg2, V arg3, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, 0, handler, 0, false,arg1, arg2, arg3, m_unTick, TimerType.Time);
    }
    
    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddTimer(uint start, int interval, Action handler, int count, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, interval, handler, count, timeChasing, m_unTick, TimerType.Time);

    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddTimer<T>(uint start, int interval, Action<T> handler, int count, T arg1, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, interval, handler, count, timeChasing, arg1, m_unTick,  TimerType.Time);

    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddTimer<T, U>(uint start, int interval, Action<T, U> handler, int count, T arg1, U arg2, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, interval, handler, count, timeChasing, arg1, arg2, m_unTick, TimerType.Time);
    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <typeparam name="V">参数类型3</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="arg3">参数3</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddTimer<T, U, V>(uint start, int interval, Action<T, U, V> handler, int count, T arg1, U arg2, V arg3, bool timeChasing = false)
    {
        return AddTimer(m_queue, start, interval, handler, count, timeChasing, arg1, arg2, arg3, m_unTick, TimerType.Time);
    }

    /// <summary>
    /// 添加单次定时帧对象
    /// </summary>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceFrameTimer(uint start, Action handler)
    {
        return AddTimer(m_FrameQueue,start, 0, handler, 0, false, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加单次帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceFrameTimer<T>(uint start, Action<T> handler, T arg1)
    {
        return AddTimer(m_FrameQueue,start, 0, handler, 0, false, arg1, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加单次帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceFrameTimer<T, U>(uint start, Action<T, U> handler, T arg1, U arg2)
    {
        return AddTimer(m_FrameQueue,start, 0, handler, 0, false, arg1, arg2, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加单次帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <typeparam name="V">参数类型3</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="arg3">参数3</param>
    /// <returns>定时对象Id</returns>
    public uint AddOnceFrameTimer<T, U, V>(uint start, Action<T, U, V> handler, T arg1, U arg2, V arg3)
    {
        return AddTimer(m_FrameQueue,start, 0, handler, 0,  false, arg1, arg2, arg3, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加帧定时对象
    /// </summary>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="interval">重复间隔，为零不重复。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddFrameTimer(uint start, int interval,Action handler, int count, bool timeChasing = false)
    {
        return AddTimer(m_FrameQueue,start, interval, handler, count, timeChasing, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="interval">重复间隔，为零不重复。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddFrameTimer<T>(uint start, int interval, Action<T> handler, T arg1, int count, bool timeChasing = false)
    {
        return AddTimer(m_FrameQueue,start, interval, handler, count, timeChasing,arg1, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="interval">重复间隔，为零不重复。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddFrameTimer<T, U>(uint start, int interval, Action<T, U> handler, T arg1, U arg2, int count, bool timeChasing = false)
    {
        return AddTimer(m_FrameQueue,start, interval, handler, count, timeChasing,arg1, arg2, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 添加帧定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <typeparam name="V">参数类型3</typeparam>
    /// <param name="start">延迟启动帧。（帧）</param>
    /// <param name="interval">重复间隔，为零不重复。（帧）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="arg3">参数3</param>
    /// <param name="count">追加调用次数, -1为永久</param>
    /// <returns>定时对象Id</returns>
    public uint AddFrameTimer<T, U, V>(uint start, int interval, Action<T, U, V> handler, T arg1, U arg2, V arg3, int count, bool timeChasing = false)
    {
        return AddTimer(m_FrameQueue,start, interval, handler, count, timeChasing, arg1, arg2, arg3, m_nFrameCount, TimerType.Frame);
    }
    
    /// <summary>
    /// 删除定时对象
    /// </summary>
    /// <param name="timerId">定时对象Id</param>
    public void DelTimer(uint timerId)
    {
        if (timerId == 0)
        {
            return;
        }
        
        //lock (m_queueLock)
            
        m_queue.Remove(timerId);
        m_FrameQueue.Remove(timerId);
        m_dict.Remove(timerId);

    }

    public void Tick(float deltaTime)
    {
        Tick((uint)(deltaTime * 1000), m_queue, ref m_unTick);
        Tick(1, m_FrameQueue,ref m_nFrameCount);
    }
    
    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="count">次</param>
    /// <returns>定时对象Id</returns>
    private uint AddTimer(KeyedPriorityQueue<uint, AbsTimerData, ulong> queue,uint start, int interval, Action handler, int count, bool timeChasing, ulong recordTime, TimerType timerType)
    {
        var p = GetTimerData(TimerData.Get(), start, interval, recordTime);
        p.Action = handler;
        p.maxCount = count;
        p.TimerType = timerType;
        p.needTimeChasing = timeChasing;
        return AddTimer(queue, p);
    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <returns>定时对象Id</returns>
    private uint AddTimer<T>(KeyedPriorityQueue<uint, AbsTimerData, ulong> queue,uint start, int interval, Action<T> handler, int count, bool timeChasing, T arg1, ulong recordTime, TimerType timerType)
    {
        var p = GetTimerData(TimerData<T>.Get(), start, interval, recordTime);
        p.Action = handler;
        p.maxCount = count;
        p.Arg1 = arg1;
        p.TimerType = timerType;
        p.needTimeChasing = timeChasing;
        return AddTimer(queue, p);
    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <returns>定时对象Id</returns>
    private uint AddTimer<T, U>(KeyedPriorityQueue<uint, AbsTimerData, ulong> queue,uint start, int interval, Action<T, U> handler, int count,bool timeChasing, T arg1, U arg2, ulong recordTime, TimerType timerType)
    {
        var p = GetTimerData(TimerData<T, U>.Get(), start, interval, recordTime);
        p.Action = handler;
        p.maxCount = count;
        p.Arg1 = arg1;
        p.Arg2 = arg2;
        p.needTimeChasing = timeChasing;
        p.TimerType = timerType;
        return AddTimer(queue, p);
    }

    /// <summary>
    /// 添加定时对象
    /// </summary>
    /// <typeparam name="T">参数类型1</typeparam>
    /// <typeparam name="U">参数类型2</typeparam>
    /// <typeparam name="V">参数类型3</typeparam>
    /// <param name="start">延迟启动时间。（毫秒）</param>
    /// <param name="interval">重复间隔，为零不重复。（毫秒）</param>
    /// <param name="handler">定时处理方法</param>
    /// <param name="arg1">参数1</param>
    /// <param name="arg2">参数2</param>
    /// <param name="arg3">参数3</param>
    /// <returns>定时对象Id</returns>
    private uint AddTimer<T, U, V>(KeyedPriorityQueue<uint, AbsTimerData, ulong> queue,uint start, int interval, Action<T, U, V> handler, int count, bool timeChasing, T arg1, U arg2, V arg3, ulong recordTime, TimerType timerType)
    {
        var p = GetTimerData(TimerData<T, U, V>.Get(), start, interval, recordTime);
        p.Action = handler;
        p.maxCount = count;
        p.Arg1 = arg1;
        p.Arg2 = arg2;
        p.Arg3 = arg3;
        p.TimerType = timerType;
        p.needTimeChasing = timeChasing;
        return AddTimer(queue, p);
    }

    private void Update()
    {
        var deltaTime = Time.deltaTime;
        Tick(deltaTime);
    }
    
    /// <summary>
    /// 周期调用触发任务
    /// </summary>
    private void Tick(uint deltaTime, KeyedPriorityQueue<uint, AbsTimerData, ulong> queue, ref ulong nTickTime)
    {
        nTickTime += deltaTime;
        
        while (queue.Count != 0)
        {
            AbsTimerData p = queue.Peek();
            if (nTickTime <= p.UnNextTick)
            {
                break;
            }
            queue.Dequeue();
            if (p.maxCount == -1 || p.recordCount < p.maxCount)
            {
                p.recordCount += 1;
                //如果需要追时间， 那么用上次结束时间+间隔再次注册
                if (p.needTimeChasing)
                {
                    p.UnNextTick += (ulong)p.NInterval;
                }
                else //如果需要追时间， 那么用上次结束时间+间隔再次注册
                {
                    p.UnNextTick = m_unTick + (ulong)p.NInterval;
                }
                queue.Enqueue(p.NTimerId, p, p.UnNextTick);
                p.DoAction();
            }
            else
            {
                m_dict.Remove(p.NTimerId);
                p.DoAction();
                p.Release();
                
            }
        }
    }

    /// <summary>
    /// 重置定时触发器
    /// </summary>
    public void Reset()
    {
        m_unTick = 0;
        m_nNextTimerId = 0;
        while (m_queue.Count != 0)
        {
            m_queue.Dequeue();
            m_FrameQueue.Dequeue();
        }
        m_dict.Clear();
    }

    private uint AddTimer(KeyedPriorityQueue<uint, AbsTimerData, ulong> queue ,AbsTimerData p)
    {
        m_dict.Add(p.NTimerId, p);
        queue.Enqueue(p.NTimerId, p, p.UnNextTick);
        return p.NTimerId;
    }

    private T GetTimerData<T>(T p, uint start, int interval, ulong recordTime) where T : AbsTimerData
    {
        p.NInterval = interval;
        p.NTimerId = ++m_nNextTimerId;
        p.UnNextTick = recordTime + start;
        return p;
    }

    //public override bool DestroyOnReboot()
    //{
    //    return true;
    //}
}
